use std::ptr::eq;

use axum_app_common::{
    error::BizError, model::SessionContext, utils::UUID, validator::RequestValidatorField,
};
use axum_app_dao::{
    entity::{
        self, rbac_role_resource_rel, sys_resource, RbacRoleResourceRelActiveModel,
        RbacRoleResourceRelColumn, RbacRoleResourceRelEntity, SysResourceActiveModel,
        SysResourceColumn,
    },
    rbac_role_resource_rel_dal, resource_dal, user_dal,
};
use sea_orm::{
    prelude::Uuid, ActiveModelTrait, ActiveValue::NotSet, DatabaseConnection, DatabaseTransaction,
    EntityTrait, InsertResult, QueryFilter, Set, TransactionTrait,
};
use sea_orm::{ColumnTrait, DeleteResult};

use tracing::{debug, error};

use crate::service::resource_svc;
pub struct ResourceSvc<'a> {
    db_conn: &'a DatabaseConnection,
    ctx: Option<&'a SessionContext>,
}

impl<'a> ResourceSvc<'a> {
    pub fn new(db_conn: &'a DatabaseConnection, ctx: Option<&'a SessionContext>) -> Self {
        Self { db_conn, ctx }
    }

    pub async fn create_resource(
        &self,
        res_type: &str,
        res_code: &str,
        res_name: &str,
        res_description: Option<&str>,
    ) -> anyhow::Result<u64> {
        let db = self.db_conn;

        let model = sys_resource::ActiveModel {
            res_type: sea_orm::ActiveValue::Set(res_type.to_string()),
            res_code: sea_orm::ActiveValue::Set(res_code.to_string()),
            res_name: sea_orm::ActiveValue::Set(res_name.to_string()),
            res_description: sea_orm::ActiveValue::Set(
                res_description.unwrap_or_default().to_string(),
            ),
            ..<axum_app_dao::entity::sys_resource::ActiveModel as ActiveModelTrait>::default()
        };

        let res = axum_app_dao::resource_dal::insert_resource(db, model).await;

        if let Ok(id) = res {
            return anyhow::Ok(id);
        }

        let conv_msg_fn = |k: String, col: &str, msg: &str| -> String {
            if k.contains(col) {
                msg.to_string()
            } else {
                k
            }
        };

        let err = res.err().unwrap();
        let err_msg = axum_app_common::error::parse_db_err_duplicate_err(err.to_string().as_str())
            .map(|(k, _)| k)
            .map(|k| conv_msg_fn(k, "sys_resource_res_code_un", "资源编码已存在"))
            .map(|k| conv_msg_fn(k, "sys_resource_name_un", "资源名称已存在"))
            .unwrap_or("数据冲突".to_string());

        anyhow::bail!(BizError::DbDuplicateErr(err_msg))
    }

    pub async fn bind_role(&self, role_code: &str, res_codes: Vec<&str>) -> anyhow::Result<()> {
        let db = self.db_conn;

        if res_codes.is_empty() {
            return Ok(());
        }

        let res_list = entity::SysResourceEntity::find()
            .filter(SysResourceColumn::ResCode.is_in(res_codes))
            .all(db)
            .await?;
        if res_list.is_empty() {
            return Ok(());
        }

        for res in res_list {
            let model = RbacRoleResourceRelActiveModel {
                res_code: Set(res.res_code),
                res_type: Set(res.res_type),
                role_code: Set(role_code.to_string()),
                ..<RbacRoleResourceRelActiveModel as ActiveModelTrait>::default()
            };
            let _ = rbac_role_resource_rel::Entity::insert(model).exec(db).await;
        }
        Ok(())
    }

    pub async fn unbind_role(&self, role_code: &str, res_codes: Vec<&str>) -> anyhow::Result<u64> {
        let db = self.db_conn;

        if res_codes.is_empty() || role_code.is_blank() {
            return Ok(0_u64);
        }

        let res = entity::RbacRoleResourceRelEntity::delete_many()
            .filter(RbacRoleResourceRelColumn::ResCode.is_in(res_codes))
            .filter(RbacRoleResourceRelColumn::RoleCode.eq(role_code))
            .exec(db)
            .await?;

        Ok(res.rows_affected)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_create_resource() {
        tracing_subscriber::fmt::init();

        let db_conn = axum_app_common::test::get_connect().await;
        let svc = ResourceSvc::new(&db_conn, None);
        let res = svc.create_resource("test", "test", "test1", None).await;

        if let Err(err) = &res {
            let root_err = err.root_cause();
            match root_err.downcast_ref::<BizError>() {
                Some(BizError::DbDuplicateErr(msg)) => eprintln!("数据冲突{}", msg),
                None => eprintln!("其他异常:{}", err),
                _ => eprintln!("其他BizErr异常:{:?}", err),
            }
        } else {
            println!("res:{}", res.unwrap());
        }
    }

    #[test]
    fn _test() {
        let conv_msg_fn = |k: String, col: &str, msg: &str| -> String {
            if k.contains(col) {
                msg.to_string()
            } else {
                k
            }
        };

        println!(
            "{}",
            conv_msg_fn(
                "sys_resource.sys_resource_name_un".to_string(),
                "sys_resource_name_un",
                "资源名称已存在"
            )
        );
    }
}
